package cc.lj.internal.coupons.application.service;

import cc.lj.internal.api.ordershare.dto.OrderOnlineAPIDTO;
import cc.lj.internal.api.ordershare.dto.OrderPaymentAPIDTO;
import cc.lj.internal.core.exception.ServiceException;
import cc.lj.internal.coupons.application.FeignAppService;
import cc.lj.internal.coupons.application.utils.TicketUtil;
import cc.lj.internal.coupons.domain.ticket.model.vo.TicketBatchDO;
import cc.lj.internal.coupons.domain.ticket.model.vo.TicketMainDO;
import cc.lj.internal.coupons.domain.ticket.model.vo.convert.TicketMainPOConvertDO;
import cc.lj.internal.coupons.domain.ticket.repository.IUseTicketCheckCacheRepository;
import cc.lj.internal.coupons.domain.ticket.service.AbstractUseTicketRuleDomainService;
import cc.lj.internal.coupons.domain.ticket.service.ITicketBatchDomainService;
import cc.lj.internal.coupons.domain.ticket.service.ITicketMainDomainService;
import cc.lj.internal.coupons.domain.ticket.service.impl.useTicketRule.FindFirstStepUtil;
import cc.lj.internal.coupons.infrastructure.excepion.CoupousExceptionEnum;
import cc.lj.internal.coupons.infrastructure.po.TicketMainPO;
import cc.lj.internal.coupons.interfaces.dto.TicketMainDTO;
import cc.lj.internal.coupons.interfaces.dto.command.UseTicketCheckCommand;
import cc.lj.internal.coupons.interfaces.dto.convert.TicketMainDTOConvertDO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * 用券验证 app service
 * @author liangxifeng
 * @date 2023/6/12 17:34
 */
@Service
@Slf4j
public class UseTicketCheckAppService {
    //电子券主表 domain service
    @Resource(name = "ticketMainDomainServiceImpl")
    private ITicketMainDomainService ticketMainDomainService;
    //电子券批次表 domain service
    @Resource(name = "ticketBatchDomainServiceImpl")
    private ITicketBatchDomainService ticketBatchDomainService;

    //用券验证 1 券状态规则
    @Resource(name = "useTicketStatusRuleDomainServiceImpl")
    private AbstractUseTicketRuleDomainService useTicketStatusRule;
    //用券验证 2 券转赠规则
    @Resource(name = "useTicketGiveRuleDomainServiceImpl")
    private AbstractUseTicketRuleDomainService useTicketGiveRule;
    //用券验证 3 券有效期规则
    @Resource(name = "useTicketDateRuleDomainServiceImpl")
    private AbstractUseTicketRuleDomainService useTicketDateRule;
    //用券验证 4 黑白名单商户规则
    @Resource(name = "useTicketBlackWhiteMerchantRuleDomainServiceImpl")
    private AbstractUseTicketRuleDomainService useTicketMerchantRule;
    //用券验证 5 满减规则
    @Resource(name = "useTicketFullReductionRuleDomainServiceImpl")
    private AbstractUseTicketRuleDomainService useTicketFullRule;
    //用券验证 6 累加规则
    @Resource(name = "useTicketMostAddRuleDomainServiceImpl")
    private AbstractUseTicketRuleDomainService useTicketMostAddRule;
    //用券验证 7 独立使用规则
    @Resource(name = "useTicketSingleRuleDomainServiceImpl")
    private AbstractUseTicketRuleDomainService useTicketSingleRule;

    //用券验证 redis 缓存仓储接口
    @Resource(name = "usetTicketCheckCacheRedisRepositoryImpl")
    private IUseTicketCheckCacheRepository cacheRepository;

    //查找每个阶梯中的第一张券 工具类
    @Resource(name = "findFirstStepUtil")
    private FindFirstStepUtil findFirstStepUtil;

    //远程请求feign
    @Resource(name = "feignAppService")
    private FeignAppService feignAppService;

    public TicketMainDTO doMain(UseTicketCheckCommand useTicketCheckCommand) {
        //通过使用码查询电子券主表数据
        TicketMainDO ticketMainDOParm = new TicketMainDO();
        //电子券使用码添加 * 号前缀
        String useCode = TicketUtil.addPrefixUseCode(useTicketCheckCommand.getTicketUseCode());
        //这里command中的电子券使用码为没有*前缀的，因为要操作redis
        useTicketCheckCommand.setTicketUseCode(TicketUtil.delPrefixUseCode(useCode));
        ticketMainDOParm.setTicketUseCode(useCode);
        List<TicketMainDO> ticketMainDOList = ticketMainDomainService.getList(ticketMainDOParm);
        TicketMainDO ticketMainDO = ticketMainDOList.size() == 1 ? ticketMainDOList.get(0) : null;
        log.info("【用券验证电子券】开始,验券command："+useTicketCheckCommand+"券主表数据:"+ticketMainDO);
        //打印该合同在redis中所有数据结构
        cacheRepository.printAll(useTicketCheckCommand.getPactNum(),useTicketCheckCommand.getUuId());
        if(ticketMainDO == null)
        {
            throw new ServiceException(CoupousExceptionEnum.GET_TICKET_MAIN_BY_USECODE_IS_NULL);
        }
        //通过合同号查询订单数据
        OrderOnlineAPIDTO orderOnlineAPIDTO = feignAppService.getOrderByPactNum(useTicketCheckCommand.getPactNum());
        //券面额 > 剩余未支付金额 AND 不与蓝景装饰合作
        if( ticketMainDO.getTicketPrice().compareTo(useTicketCheckCommand.getSurplusPay()) == 1
                && orderOnlineAPIDTO.getDecorationStatus() == 0)
        {
            throw new ServiceException(CoupousExceptionEnum.USE_TICKET_PRICE_ERROR);
        }

        //判断UUID如果为空则生成
        if( useTicketCheckCommand.getUuId() == null || useTicketCheckCommand.getUuId().trim().isEmpty() )
        {
            String uuid = UUID.randomUUID().toString().replace("-", "");
            log.info("【用券验证电子券】合同:"+useTicketCheckCommand.getPactNum()+",第一次用券,uuid="+uuid);
            useTicketCheckCommand.setUuId(uuid);
        }
        //验证电子券是否已经存在于redis中
        cacheRepository.checkTicketIsUse(TicketMainPOConvertDO.INSTANCE.convert(ticketMainDO),useTicketCheckCommand);

        //通过主键查询电子券批次表
        TicketBatchDO ticketBatchDO = ticketBatchDomainService.getById(ticketMainDO.getTicketBatchId());

        //2.券转赠规则,券使用状态的下一个责任
        useTicketStatusRule.setNextUseTicketRule( useTicketGiveRule );
        //3.券有效期规则,券转赠规则的下一个责任
        useTicketGiveRule.setNextUseTicketRule( useTicketDateRule );
        //4.券黑白名单规则,有效期规则的下一个责任
        useTicketDateRule.setNextUseTicketRule( useTicketMerchantRule );
        //5.券满减规则,有券黑白名单规则的下一个责任
        useTicketMerchantRule.setNextUseTicketRule(useTicketFullRule);
        //6.累加规则,券满减规则的下一个责任
        useTicketFullRule.setNextUseTicketRule(useTicketMostAddRule);
        //7.独立使用规则,券累加规则的下一个责任
        useTicketMostAddRule.setNextUseTicketRule(useTicketSingleRule);


        //1.责任链的第一个元素开始验证(券使用状态）
        useTicketStatusRule.checkRule(ticketBatchDO,ticketMainDO,useTicketCheckCommand,orderOnlineAPIDTO);


        /**
         * 所有券规则验证通过后需要 将该张券新增到 redis 已用券列表中
         * key1 = use_ticket_合同号_UUID (HASH), value1 = {券使用码:券批次主键}
         * --------------------------------------------------------------
         * key2 = use_batch_合同号_UUID(HASH), value2 =  {券批次主键 : 使用张数}
         * --------------------------------------------------------------
         * key4 = global_used_券使用码_合同号_uuid(String 类型，目的：排除一张券在多个端用）,value = 券批次主键
         * --------------------------------------------------------------
         * key3 = batch_other_合同号_UUID (HASH), value = { single_batch_id: 独立使用的券批次主键, limit_value: 满减剩余额度空间)
         */
        //如果该券是 阶梯中的第一张券，则需要减小满减额度在redis中的值
        if( findFirstStepUtil.isStepFirstTicket(ticketBatchDO,useTicketCheckCommand.getPactNum(),useTicketCheckCommand.getUuId(),1) ) {
            //从redis中拿到满减剩余额度
            BigDecimal limitValue = getLimitValue(useTicketCheckCommand,orderOnlineAPIDTO);
            //新满减剩余空间 = 原满减剩余空间 - 此电子券占的额度
            BigDecimal newLimitValue = limitValue.subtract(ticketBatchDO.getConditionValue());
            Map<String,Object> limitValueMap = new HashMap<>();
            limitValueMap.put("limit_value",newLimitValue.toString());
            //设置 key3
            cacheRepository.setUseTicketOther(useTicketCheckCommand.getPactNum(),useTicketCheckCommand.getUuId(),limitValueMap);
            log.info("【用券验证电子券】：该券是阶梯中的第一张券，redis中剩余额度空间设置成功");
        }
        // 设置 key1, key2, key4
        TicketMainPO ticketMainPO = TicketMainPOConvertDO.INSTANCE.convert(ticketMainDO);
        cacheRepository.addUseTicket(ticketMainPO,useTicketCheckCommand);
        log.info("【用券验证电子券】redis中已用券列表设置成功");
        TicketMainDTO ticketMainDTO = TicketMainDTOConvertDO.INSTANCE.convert(ticketMainDO);
        ticketMainDTO.setUuId(useTicketCheckCommand.getUuId());
        log.info("【用券验证电子券】全部规则验证通过,验券command："+useTicketCheckCommand+"返回券主表数据:"+ticketMainDTO);
        return ticketMainDTO;
    }
    /**
     * 计算满减剩余空间
     */
    private BigDecimal getLimitValue(UseTicketCheckCommand useTicketCheckCommand,OrderOnlineAPIDTO orderOnlineAPIDTO) {
        //从redis中读取券批次使用数量 <券批次主键，已用券数量>
        Map<Integer,Integer> usedBatchNumHash = cacheRepository.getUseBatch(useTicketCheckCommand.getPactNum(),useTicketCheckCommand.getUuId());
        BigDecimal limitValue; //满减剩余空间
        //该合同第一次用券, 满减剩余空间 = 订单应付额
        if( usedBatchNumHash.isEmpty()) {
            //获取订单支付方式数据
            List<OrderPaymentAPIDTO> paymentList = orderOnlineAPIDTO.getOrderPaymentDTOList();
            OrderPaymentAPIDTO orderPaymentAPIDTO = paymentList.get(paymentList.size()-1); //取最后一条数据
            limitValue = orderPaymentAPIDTO.getAccountPayableDiscount();
        }else {
            //缓存中用券其他数据 <single_batch_id: 独立使用的券批次主键, limit_value: 满减剩余额度空间>
            Map<String,Object> useTicketOherData = cacheRepository.getUseTicketOther(useTicketCheckCommand.getPactNum(),useTicketCheckCommand.getUuId());
            limitValue = new BigDecimal( useTicketOherData.get("limit_value").toString() );
        }
        return limitValue;
    }
}
